package com.gmrz.uaf.protocol.v1.json;

import com.gmrz.uaf.common.TLVTag;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.uaf.protocol.v1.schema.ASMSignResponse;
import com.gmrz.uaf.protocol.v1.schema.CertAuthTransaction;
import com.gmrz.uaf.protocol.v1.schema.SignData;
import com.gmrz.util.Convert;
import com.gmrz.util.db.CommonUtils;
import com.google.gson.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;

public class CertASMSignResponseAdapter
        implements InstanceCreator<ASMSignResponse>, JsonSerializer<ASMSignResponse>, JsonDeserializer<ASMSignResponse> {
    private static final Logger LOG = LogManager.getLogger(CertASMSignResponseAdapter.class);

    public ASMSignResponse createInstance(Type type) {
        return new ASMSignResponse();
    }

    public JsonElement serialize(ASMSignResponse src, Type typeofSrc, JsonSerializationContext context) {
        return null;
    }

    public ASMSignResponse deserialize(JsonElement json, Type typeofT, JsonDeserializationContext context)
            throws JsonParseException {
        try {
            String encoded = json.getAsString();
            ByteBuffer buffer = ByteBuffer.wrap(Convert.fromBase64(encoded)).order(ByteOrder.LITTLE_ENDIAN);

            TLVTag responseTag = new TLVTag(TLVTag.readTag(buffer));//2字节标签
            byte[] responseBytes = responseTag.parseByteArray(buffer);

            ByteBuffer resBuffer = ByteBuffer.wrap(responseBytes).order(ByteOrder.LITTLE_ENDIAN);
            ASMSignResponse retval = new ASMSignResponse();
            SignData signData = null;

            while (resBuffer.hasRemaining()) {
                byte[] tagByte = new byte[2];
                resBuffer.get(tagByte);
                int tag = Convert.byteArrayToInt(tagByte);
                byte[] lenByte = new byte[2];
                resBuffer.get(lenByte);
                int len = Convert.byteArrayToInt(lenByte);
                byte[] val = new byte[len];
                resBuffer.get(val);
                switch (tag) {
                    case 21251: {
                        byte[] signatureBytes = val;
                        retval.setSignature(signatureBytes);
                        break;
                    }
                    case 21767: {
                        byte[] certBytes = val;
                        signData = getSignData(val);
                        retval.setSignedData(signData);
                        break;
                    }
                    case 21008: {
                        byte[] p7Bytes = val;
                        retval.setP7Bytes(p7Bytes);
                        break;
                    }

                }
            }

            return retval;
        } catch (Exception ex) {
            LOG.error("Error parsing the Authentication TLV data, ", ex);
            throw new JsonParseException(UAFErrorCode.PROTOCOL_TLV_PARSING_FAILED.toString(), ex);
        }
    }

    private SignData getSignData(byte[] signedDataBytes){
        ByteBuffer signedDataBuffer = ByteBuffer.wrap(signedDataBytes).order(ByteOrder.LITTLE_ENDIAN);
        SignData signData = new SignData();
        signData.setReceivedBytes(signedDataBytes);
        while (signedDataBuffer.hasRemaining()) {
            byte[] tagByte = new byte[2];
            signedDataBuffer.get(tagByte);
            int tag = Convert.byteArrayToInt(tagByte);
            byte[] lenByte = new byte[2];
            signedDataBuffer.get(lenByte);
            int len = Convert.byteArrayToInt(lenByte);
            byte[] val;
            switch (tag) {
                case 21010://TRANSCATIONCONTENT_HASH
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    signData.withTransactionTextHash(val);
                    break;
                case 20995://FINALCHALLENGE
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    signData.withHashedFinalChallenge(val);
                    break;
                case 21765://COUNTER
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    ByteBuffer counterBuf = ByteBuffer.wrap(val).order(
                            ByteOrder.LITTLE_ENDIAN);
                    signData.withSignCounter(counterBuf.getShort(2));
                    break;
                case 21766://KEYID
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    signData.withKeyID(val);
                    break;
                case 21013://uvi
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    signData.withUvi(val);
                    break;
                case 21003://aaid
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    signData.withAAID(new String(val));
                    break;
                case 21014://uvs
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    if (len > 0 && (len % 32) == 0) {
                        List<byte[]> uvs = new ArrayList<byte[]>(len / 32);
                        for (int i = 0; i < len / 32; i++) {
                            byte[] uvi = new byte[32];
                            System.arraycopy(val, i * 32, uvi, 0, 32);
                            uvs.add(uvi);
                        }
                        signData.setUvs(uvs);
                    }
                    break;
                case 35331://CERT_AUTH_TRADE_ISHOW
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    signData.setCertAuthTradeIshow(val);
                    break;
                case 35348://CERT_AUTH_TRADE_SHOWDATA_HASH
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    signData.setCertAuthTradeShowdataHash(val);
                    break;
                case 22529://TAG_CERT_IS_DEVICEKEY_SIGN 0x5801
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    signData.setTagCertIsDeviceKeySign(val);
                    break;
                case 35329://CertAuthTradeIshow
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    CertAuthTransaction certAuthTransaction = getCertAuthTransaction(val);
                    signData.setCertAuthTradeIshow(certAuthTransaction.getCertAuthTradeIshow());
                    break;
                case 21252://DeviceID
                    val = new byte[len];
                    signedDataBuffer.get(val);
                    signData.setDeviceID(Convert.toBase64(val));
                    break;
            }
        }
        return signData;
    }

    private CertAuthTransaction getCertAuthTransaction(byte[] valBytes){
        ByteBuffer certAuthTransactionBuffer = ByteBuffer.wrap(valBytes).order(ByteOrder.LITTLE_ENDIAN);
        CertAuthTransaction certAuthTransaction = new CertAuthTransaction();
        certAuthTransaction.setReceivedBytes(valBytes);
        while (certAuthTransactionBuffer.hasRemaining()) {
            byte[] tagByte = new byte[2];
            certAuthTransactionBuffer.get(tagByte);
            int tag = Convert.byteArrayToInt(tagByte);
            byte[] lenByte = new byte[2];
            certAuthTransactionBuffer.get(lenByte);
            int len = Convert.byteArrayToInt(lenByte);
            byte[] val;
            switch (tag) {
                case 35330:
                    val = new byte[len];
                    certAuthTransactionBuffer.get(val);
                    certAuthTransaction.setCertAuthTradeBase(val);
                    break;
                case 35331:
                    val = new byte[len];
                    certAuthTransactionBuffer.get(val);
                    certAuthTransaction.setCertAuthTradeIshow(val);
                    break;
                case 35332:
                    val = new byte[len];
                    certAuthTransactionBuffer.get(val);
                    certAuthTransaction.setCertAuthTradeShowdata(val);
                    break;
            }
        }
        return certAuthTransaction;
    }

    public static void main(String[] args){
        CertASMSignResponseAdapter certASMSignResponseAdapter = new CertASMSignResponseAdapter();
        certASMSignResponseAdapter.deserialize(null,null,null);
    }
}
